<?php

namespace App\Console\Commands\Feishu;

use App\Exceptions\BaseException;
use App\Kuafu\Config\Model\ConfigModel;
use App\Kuafu\Config\Service\ConfigService;
use App\Kuafu\Feishu\Model\FsDepartmentsModel;
use App\Kuafu\Feishu\Model\FsUserModel;
use App\Kuafu\Nsq\Enum\NsqCodeEnum;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\User\Enum\CodeEnum;
use App\Kuafu\User\Enum\RoleCodeEnum;
use App\Kuafu\User\Enum\UserCodeEnum;
use App\Kuafu\User\Model\Role;
use App\Kuafu\User\Model\User;
use App\Kuafu\User\Model\UserRoleMapping;
use App\Kuafu\User\Service\AuthService;
use App\Kuafu\User\Service\UserService;
use App\Library\FeiShu\SendFs;
use App\Remote\Feishu\Departments\DepartmentsRemote;
use App\Remote\Feishu\User\TenantAccessTokenRemote;
use App\Remote\Feishu\User\UserRemote;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Kuafu\GeneralFs\BuildFs;
use Psr\SimpleCache\InvalidArgumentException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class SynchronizeUserinfo extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'fs:SynchronizeUserinfo';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '同步飞书用户信息';

    /**
     * 待更新sql
     * @var array
     */
    private array $updateData = [];

    /**
     * 自动创建的账号
     * @var string
     */
    private string $createdUsers = '';

    /**
     * 新增的fs_users
     * @var array
     */
    private array $newFsUsers = [];

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    public array $departments = [];

    public int $executeTime = 0;

    /**
     * Execute the console command.
     *
     * @throws BaseException
     * @throws GuzzleException
     */
    public function handle()
    {
        $this->executeTime = time();
        $lockKey           = 'SynchronizeUserinfoCommand';
        if (!Redis::setnx($lockKey, time())) {

            // 防止死锁
            $lock_time = Redis::get($lockKey);
            if (($lock_time + 300) < time()) {
                Redis::del($lockKey);
            } else {
                $this->writeInfo('脚本正在执行中，请勿重复执行');
                return;
            }
        }

        try {
            $this->doHandle();
        } catch (\Exception $e) {
            throw $e;
        } finally {
            Redis::del($lockKey);
        }
    }

    private string $tenantAccessToken;

    private function doHandle()
    {

        $tenantAccessToken       = TenantAccessTokenRemote::accessToken(UserCodeEnum::MERCHANT_TYPE);
        $this->tenantAccessToken = $tenantAccessToken["tenant_access_token"];

        //同步部门信息
        try {
//            $this->output->comment("同步部门信息 ... 开始");
            $this->synchronizeFsDepartments($tenantAccessToken["tenant_access_token"]);
//            $this->output->comment("同步部门信息 ... 结束");
        } catch (\Exception $exception) {
            logException("同步飞书部门信息失败", $exception, null, true, true);
        }

        try {
            $departmentUser = collect($this->departmentUser());
            FsUserModel::query()->get()->each(function (FsUserModel $fsUserModel) use ($departmentUser) {
                $userDepartment = [];
                $departmentUser->each(function ($department) use ($fsUserModel, &$userDepartment) {
                    $user = collect($department["user"])->where("userId", $fsUserModel->userId)->first();
                    if ($user) {
                        $userDepartment = $department["department"];
                        return false;
                    }
                });
                $fsUserModel->groupParentDepartment = implode("/", array_column($userDepartment, "name"));
                $fsUserModel->save();
            });
        } catch (\Exception $exception) {
            logException("重构用户父级部门失败", $exception, null, true, true);
        }

        $this->pageDepartments(null, $tenantAccessToken["tenant_access_token"], true);

        // 更新fs_users信息
        $ids = [];
        foreach ($this->updateData as $arr) {
            $fsUserModel = FsUserModel::query()->updateOrCreate($arr[0], $arr[1]);

            // 自动创建账号
            $this->createUser($fsUserModel);

            // 更新到的ids
            $ids[] = $fsUserModel->id;
        }

        $doNotAutoDeleteUserList = ConfigModel::getConfigByTypeKeyOne('fsUser', 'doNotAutoDelete');
        $doNotAutoDeleteUserList = json_decode($doNotAutoDeleteUserList['value'], true);
        $nDUserListIds           = array_column($doNotAutoDeleteUserList, 'id');

        // 没更新到的大概率是离职了 - 挨个查用户信息
        FsUserModel::query()
                   ->whereNotIn('id', array_merge($ids, $nDUserListIds))
                   ->where('isExited', 0)
                   ->get()
                   ->each(function (FsUserModel $fsUserModel) use ($tenantAccessToken) {

                       try {

                           // 单独查询用户信息
                           $item = UserRemote::userinfoBySsoToken('open_id', $tenantAccessToken["tenant_access_token"],
                               $fsUserModel->openId);
                           if (isset($item['user']) && $item['user']) {

                               // 用户关联的departmentIds
                               $onlineDepartmentIds = $item['user']['department_ids'] ?? [];

                               // 换部门了，这行删了
                               if (!in_array($fsUserModel->departmentId, $onlineDepartmentIds)) {
                                   $fsUserModel->delete();

                                   return;
                               }

                               // 更新 - 大概率会离职
                               $fsUserModel->update($this->convertUserItem($item['user'], []));
                           }

                       } catch (\Exception $e) {
                           Log::warning($e);
                       }

//                       $stop = true;
//                       // 兼容换部门的情况，只要找到一行userId启用记录就算在职
//                       foreach ($this->updateData as $val) {
//                           if ($fsUserModel->userId == $val[0]['userId']) {
//                               $stop = false;
//                               break;
//                           }
//                       }
//
//                       if ($stop) {
//
//                           // 自动停用账号
//                            $this->stopUser($fsUserModel);
//                       }
                   });

        // 删除在职员工的isExited的记录
        FsUserModel::query()->where('isExited', 1)->get()->each(function ($item) {
            $online = FsUserModel::query()->where('userId', $item->userId)->where('isExited', 0)->count();
            if ($online) {
                $item->delete();

                $this->writeInfo("delete ... {$item->id} {$item->name}");
            }
        });

        // 新增员工 - 发布nsq
        if ($this->createdUsers) {
            (new \Kuafu\Nsq\NsqPublisher(config('nsq.default.address')))
                ->pub(NsqCodeEnum::FS_USER_NEW, ['users' => $this->newFsUsers]);
        }

        $content = $this->createdUsers;

        $sendFs = BuildFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)
                        ->setTitleContent(config('app.env') . '飞书用户同步完成', $content);

        // 指定prod的业务机器人
        if (config('app.env') == 'pre') {
            $sendFs->setRobotConfig('https://open.feishu.cn/open-apis/bot/v2/hook/efe90cd8-d3cd-439f-852b-5b68ec432e25',
                'wV4bfqOm9fw8M5UIMS2hhd');
        }

        if ($content && !isTest()) {
            $sendFs->setAtUid('e119e19f'); // 扶苏
        }

        $sendFs->send();
    }

    /**
     * @param string $tenantAccessToken
     * @throws BaseException
     * @throws GuzzleException
     */
    public function synchronizeFsDepartments(string $tenantAccessToken)
    {
        FsDepartmentsModel::query()->truncate();
        $this->synchronizeDepartments(null, $tenantAccessToken, false);

        Log::info("synchronizeFsDepartments:", $this->departments);

        collect($this->departments)->each(function ($department) {
            tap(
                FsDepartmentsModel::query()->firstOrNew(
                    ["openDepartmentId" => $department["openDepartmentId"]],
                    array_merge($department, ["createdAt" => $this->executeTime])
                ),
                function ($instance) use ($department) {
                    $instance->fill(array_merge($department, [
                        "updatedAt" => $this->executeTime
                    ]))->save();
                });
        });
    }

    /**
     * @param string|null $departmentId
     * @param string $tenantAccessToken
     * @param bool $fetchChild
     * @param string $level
     * @throws BaseException
     * @throws GuzzleException
     */
    public function synchronizeDepartments(
        ?string $departmentId,
        string $tenantAccessToken,
        bool $fetchChild,
        string $level = " --> "
    ) {
        $departments = DepartmentsRemote::departments($departmentId, $tenantAccessToken, $fetchChild);
        if (isset($departments["items"])) {
            collect($departments["items"])->each(function ($department) use ($level, $tenantAccessToken, $fetchChild) {
                array_push($this->departments, [
                    "departmentId"       => $department["department_id"],
                    "openDepartmentId"   => $department["open_department_id"],
                    "leaderUserId"       => $department["leader_user_id"] ?? "",
                    "chat_id"            => $department["chat_id"] ?? "",
                    "memberCount"        => $department["member_count"],
                    "name"               => $department["name"],
                    "parentDepartmentId" => $department["parent_department_id"],
                    "order"              => $department["order"],
                    "isDeleted"          => (int)$department["status"]["is_deleted"]
                ]);

//                $this->output->info(vsprintf("%s %s (%s)", [$level, $department["name"], $department["member_count"]]));
                $this->synchronizeDepartments(
                    $department["open_department_id"],
                    $tenantAccessToken,
                    $fetchChild,
                    $level . " --> "
                );
            });
        }
        if ($departments["has_more"]) {
            $this->synchronizeDepartments($departmentId, $tenantAccessToken, $fetchChild, $departments["page_token"]);
        }
    }

    private function convertUserItem(array $item, array $department = []): array
    {
        $data = [
            "userId"          => $item["user_id"],
            "name"            => $item["name"],
            "nickname"        => $item["nickname"] ?? "",
            "email"           => $item["email"],
            "mobile"          => $mobile = str_replace("+86", "", $item["mobile"]),
            'rmsUserId'       => $this->getRmsUserIdByMobile($mobile),
            "gender"          => $item["gender"],
            "avatar"          => $item["avatar"]["avatar_origin"],
            "openId"          => $item["open_id"],
            "unionId"         => $item["union_id"],
            "isFrozen"        => (int)$item["status"]["is_frozen"],
            "isResigned"      => (int)$item["status"]["is_resigned"],
            "isActivated"     => (int)$item["status"]["is_activated"],
            "isExited"        => (int)$item["status"]["is_resigned"], // 用is_exited不准，是主动退出的含义。但是我们代码中，都是用isExited表示离职
            "isUnJoin"        => (int)$item["status"]["is_unjoin"],
            "joinTime"        => $item["join_time"],
            "isTenantManager" => (int)$item["is_tenant_manager"],
            "employeeNo"      => $item["employee_no"],
            "employeeType"    => $item["employee_type"],
//            "departmentId"       => $department["open_department_id"],
//            "departmentName"     => $department["name"],
//            "parentDepartmentId" => $department['parent_department_id'],
//            "rootDepartmentId"   => $rootDepartmentId = $this->getRootDepartmentId($department["open_department_id"],
//                $tenantAccessToken),
            "jobTitle"        => $item["job_title"],
        ];

        if ($department) {
            $data = array_merge($data, [
                "departmentId"       => $department["open_department_id"],
                "departmentName"     => $department["name"],
                "parentDepartmentId" => $department['parent_department_id'],
                "rootDepartmentId"   => $this->getRootDepartmentId($department["open_department_id"],
                    $this->tenantAccessToken),
            ]);
        }

        return $data;
    }

    /**
     * @param string|null $departmentId
     * @param string $tenantAccessToken
     * @param bool $fetchChild
     * @param string $pageToken
     * @throws BaseException
     * @throws GuzzleException
     */
    public function pageDepartments(
        ?string $departmentId,
        string $tenantAccessToken,
        bool $fetchChild = false,
        string $pageToken = ""
    ) {
        $departments = DepartmentsRemote::departments($departmentId, $tenantAccessToken, $fetchChild, $pageToken);

        if ($departments["items"]) {
            foreach ($departments["items"] as $key => $department) {
                $this->writeInfo(
                    collect($department)
                        ->only(["department_id", "name", "member_count"])
                        ->toJson(JSON_UNESCAPED_UNICODE)
                );

                $users = UserRemote::getUserByDepartment($department["open_department_id"], $tenantAccessToken);
                if (isset($users["items"])) {
                    foreach ($users["items"] as $item) {

                        // 转换user结构体
                        $date             = $this->convertUserItem($item, $department);
                        $rootDepartmentId = $date['rootDepartmentId'];

                        if ($rootDepartmentId != $department["open_department_id"]) {
                            $date['rootDepartmentName'] = Cache::store('array')
                                                               ->remember("DepartmentDetail:{$rootDepartmentId}", 600,
                                                                   function () use (
                                                                       $rootDepartmentId,
                                                                       $tenantAccessToken
                                                                   ) {
                                                                       return DepartmentsRemote::departmentInfo($rootDepartmentId,
                                                                           $tenantAccessToken)['department']['name'] ?? '';
                                                                   });
                        } else {
                            $date['rootDepartmentName'] = $department["name"];
                        }

                        // 更新的数据
                        $this->updateData[] = [
                            ['userId' => $item['user_id'], 'departmentId' => $department["open_department_id"]],
                            $date,
                        ];
                    }
                }
            }
        }
        if ($departments["has_more"]) {
            $this->pageDepartments($departmentId, $tenantAccessToken, $fetchChild, $departments["page_token"]);
        }
    }

    /**
     * 获取一级部门id
     * @param string $departmentId
     * @param string $tenantAccessToken
     * @return string
     */
    private function getRootDepartmentId(string $departmentId, string $tenantAccessToken): string
    {
        // 更新一级部门id
        $parentDepartments = Cache::store('array')
                                  ->remember("Cache:getParentDepartmentId:{$departmentId}", 1000,
                                      function () use ($departmentId, $tenantAccessToken) {
                                          return DepartmentsRemote::parentDepartments($departmentId,
                                              $tenantAccessToken);
                                      });

        if (!isset($parentDepartments['items'])) {
            return $departmentId;
        }

        foreach ($parentDepartments['items'] as $item) {
            if ($item['parent_department_id'] == "0") {
                return $item['open_department_id'];
            }
        }

        return $departmentId;
    }

    private function getRmsUserIdByMobile(string $mobile): int
    {
        if (!$mobile) {
            return 0;
        }

        return (int)User::query()->where('mobile', $mobile)->value('id');
    }

    /**
     * 自动创建user表信息
     * @param FsUserModel $fsUserModel
     * @return void
     */
    private function createUser(FsUserModel $fsUserModel)
    {
        if ($fsUserModel->rmsUserId) {
            return;
        }

        $user = User::query()->where('mobile', $fsUserModel->mobile)->first();
        if ($user) {
            $user->feishuUserId = $fsUserModel->userId;
            $user->save();

            return;
        }

        if ($fsUserModel->nickname) {
            $name = $fsUserModel->name . "（{$fsUserModel->nickname}）";
        } else {
            $name = $fsUserModel->name;
        }
        $name = str_replace([' ', '.', '·'], '', $name);

        // 新建员工账号时 判断各分公司选址开发人员 创建【开店】省区开发 权限
        $roleList = [];
        if ($fsUserModel->rootDepartmentName == '分公司' && $fsUserModel->jobTitle == '区域开发经理') {
            $roleId = Role::query()->where('name', '【开店】省区开发')->value('id');
            if ($roleId) {
                $roleList = ["brandRoles" => [$roleId], "shopRoles" => [], "merchantRoles" => []];
            }
        }
        try {
            list($userId, $_) = UserService::createOrRestore(1, $fsUserModel->mobile, $name, 'Kf123456', $roleList);
            $fsUserModel->rmsUserId = $userId;
            $fsUserModel->save();

            User::query()->where('id', $userId)->update(['feishuUserId' => $fsUserModel->userId]);

            $this->createdUsers .= "新建账号：{$name} {$fsUserModel->mobile}" . PHP_EOL;

            $this->newFsUsers[] = $fsUserModel->toArray();
        } catch (\Exception $e) {
            Log::warning($e);
        }
    }

    /**
     * 自动停用账号
     * @param FsUserModel $fsUserModel
     * @return string
     */
    public static function stopUser(FsUserModel $fsUserModel): string
    {
        if (!$fsUserModel->rmsUserId) {
            return '';
        }

        $user = User::query()->where('id', $fsUserModel->rmsUserId)->first();
        if (!$user) {
            return '';
        }

        if ($user->status == CodeEnum::USER_STATUS_STOP) {
            return '';
        }

        // 检查是否有任意 商家app店长权限
        $checkMerchantManager = UserRoleMapping::query()
                                               ->where('userId', $fsUserModel->rmsUserId)
                                               ->where('roleId', config('merchant.manager_role_id'))
                                               ->where('shopId', '!=', 1) // 数研测试店
                                               ->first();

        if ($checkMerchantManager) {

            // 解绑运营中心权限
            UserRoleMapping::query()
                           ->where('userId', $fsUserModel->rmsUserId)
                           ->where('type', RoleCodeEnum::ROLE_WEB_TYPE)
                           ->update(['isDeleted' => 1]);

            return "运营中心权限被解绑： {$user->username} {$user->mobile}" . PHP_EOL;

        } else {

            // 停用
            UserService::setUserStatus($user->id, CodeEnum::USER_STATUS_STOP);

            return "账号被停用： {$user->username} {$user->mobile}" . PHP_EOL;
        }
    }

    private function writeInfo(string $text)
    {
        if (app()->runningInConsole()) {
            $this->info($text);
        } else {
            dump($text);
        }
    }


    /**
     * 获取部门用户信息
     *
     * @return array
     */
    public function departmentUser(): array
    {
        $fsDepartments = FsDepartmentsModel::query()->with(["userRel"])->select([
            "openDepartmentId",
            "parentDepartmentId",
            "name",
            "memberCount",
            "order"
        ])->get()->keyBy("openDepartmentId")->toArray();

        $tree = [];
        foreach ($fsDepartments as &$department) {
            if (!empty($department["parentDepartmentId"])) {
                $fsDepartments[$department["parentDepartmentId"]]["child"][] = &$department;
            } else {
                $tree[] = &$department;
            }
        }

//        file_put_contents(storage_path("eg.log"),collect($tree)->toJson(JSON_UNESCAPED_UNICODE));

        unset($fsDepartments);
        $departmentUser = [];
        foreach ($tree as $treeDepartment) {
            $this->build(
                $treeDepartment,
                $departmentUser,
                [collect($treeDepartment)->only(["openDepartmentId", "name"])->toArray()]
            );
        }

//        file_put_contents(storage_path("eg1.log"),collect($departmentUser)->toJson(JSON_UNESCAPED_UNICODE));
        return $departmentUser;
    }

    /**
     * 构建部门用户数据
     *
     * @param array $department
     * @param array $departmentUser
     * @param array $groupDepartment
     */
    public function build(array $department, array &$departmentUser, array $groupDepartment)
    {
        if (isset($department["child"])) {
            foreach ($department["child"] as $childDepartment) {
                $tempGroupDepartment = array_merge(
                    $groupDepartment,
                    [collect($childDepartment)->only(["openDepartmentId", "name"])->toArray()]
                );
                $this->build(
                    $childDepartment,
                    $departmentUser,
                    $tempGroupDepartment
                );
            }
        } else {
            array_push($departmentUser, [
                "department" => $groupDepartment,
                "user"       => $department["user_rel"]
            ]);
        }
    }
}
